require "genelist"
require "chromloc"

class Gene

	@@GeneLocHash = GeneListControl.new("gene_loc.txt")

	@@segments = 3
	@@NA = "NA"
	@@max_probes = 0


	class LocSeg < Struct.new(:loc, :probes); end;

	class ProbeGroup < Array
		def group_to_s(attribs)
			att_mapped = attribs.map do |attrib|
				all_segs = self.map do |seg|
					if( seg.probes.empty?)
						$NA
					else
						seg.probes.map { |p| p.send(attrib.to_s.downcase) }.join(";")
					end
				end

				all_segs.join("\t")
			end

			att_mapped.join("\t\t") + "\n"
		end
	end


	def self.segments; @@segments; end;
	def self.group_count; @@segments + 2; end;

	def self.reset
		@@max_probes = 0
	end

	def self.reload_loclists
		@@GeneLocHash = GeneListControl.new("gene_loc.txt")
		Probe.reload_probe_locs
	end

	attr_reader :gene_name, :probes, :attributes, 
		:max_probes, :gene_loc, :probe_groups, :probe_count

	def initialize(gene_name, attrib = [], probe_output = [])
		@gene_name = gene_name
		@attributes = attrib

		@probes = {}
		@probe_output = probe_output

		loc_dat = @@GeneLocHash.get_dat(@gene_name)
		if(loc_dat)
			loc_dat.reverse! if (loc_dat.delete_at(2).to_i == -1)
			self.gene_loc = ChromLoc.new(loc_dat[0].to_i, loc_dat[1].to_i)
		end
	end

	def add_probe_data(probe_name, value)
		if(@probes.has_key? probe_name)
			@probes[probe_name].add_data( value )
		else
			new_probe = Probe.new( probe_name, value ) 
			@probes[probe_name] = new_probe
			group_probe(new_probe) if new_probe.loc_obj

			@@max_probes = @probes.size if(@probes.size > @@max_probes)
		end
	end

	def add_attribute( attr_name, attribute )
		@attributes << [attr_name, attribute]
	end

	def gene_loc=(in_loc)
		if(in_loc)
			@gene_loc = in_loc
			segment_probes
		end
	end

	def segment_probes
		seg_range = @gene_loc.true_range / @@segments
		curr_loc = @gene_loc.start

		@probe_groups = ProbeGroup.new

		# Add the 5'UTR segment
		@probe_groups << LocSeg.new(ChromLoc.new(0, 0), [])

		# Create the locs for each segment
		@@segments.times do
			@probe_groups << LocSeg.new(ChromLoc.new(@gene_loc.chrom, curr_loc, curr_loc + seg_range), [])
			curr_loc += seg_range
		end

		# Add the 3'UTR segment
		@probe_groups << LocSeg.new(ChromLoc.new(0, 0), [])
	end

	def group_probes
		# Place the probes into the locs
		puts "Grouping probes..."
		@probes.values.each { |p| group_probe(p) }
	end

	# Place the probe into a group
	def group_probe(probe)
		return nil unless(probe.loc_obj && @gene_loc)
		return nil if(probe.loc_obj.is_forward? != @gene_loc.is_forward?)

		@probe_groups.each_with_index do |pg, i|
			if (pg && (probe.loc_obj.is_maj_contained? pg.loc))
				pg.probes << probe 
				return pg
			end
		end

		if(probe.loc_obj.partly_before? @gene_loc)
			#puts "5\'UTR, #{probe.loc_obj.is_forward?}:\tP-#{probe.loc_obj.start}\t#{probe.loc_obj.end}\t\tG-#{gene_loc.start}\t#{gene_loc.end}"
			@probe_groups.first.probes << probe; 
			return @probe_groups.first
		end

		if(probe.loc_obj.partly_after? @gene_loc)
			@probe_groups.last.probes << probe;
			return @probe_groups.last
		end

		# debugger
		$LOG.warn("Couldn't find a group for probe #{probe.probe_name}...")

	end

	def probe_value_header
		ret_a = []
		0.upto(@@max_probes - 1) do |i|
			ret_a << "LogRatio" + i.to_s
		end
		ret_a
	end

	def header
		header = ["SystematicName"].concat(@attributes.map {|att| att[0]})
		header.concat(@probe_output.map do |p| 
			if !(p[1] == "\t")
				p[0].to_s
			else
				self.send(p[0].to_s.downcase + "_header")
			end
		end)
		return header.join("\t")
	end

	def probe_avg
		total = 0
		@probes.values.each {|p| total += p.probe_value}
		return (total / @probes.size)
	end

	def to_s
		# Basic setup of the gene name and the attributes
		temp_a = [@gene_name].concat(@attributes.map {|att| eval att[1]} )

		# Sort the probes
		sort_probes = @probes.values.sort
		probe_out = @probe_output.map do |pair|
			sort_probes.map {|p| p.send( pair[0].to_s.downcase)}.join( pair[1] )
		end

		temp_a.concat probe_out
		# Add NA's
		temp_a.concat Array.new(@@max_probes - @probes.size, @@NA)

		return temp_a.join("\t")
	end


	class Probe

		@@ProbeLocHash = GeneListControl.new

		def self.reload_probe_locs
			@@ProbeLocHash = GeneListControl.new
		end

		attr_reader :probe_name, :probe_value, :probes, :total, :chrom_loc, :loc_obj,
			:all_probe_count

		def initialize(probe_name, value)
			@probe_name = probe_name
			@chrom_loc = @@ProbeLocHash.get_dat( @probe_name )[0] 

			if( ChromLoc.valid_string?(@chrom_loc) )
				@loc_obj = ChromLoc.new(@chrom_loc)
			else
				@loc_obj = nil
			end

			@probes = 0
			@total = 0
			add_data(value)
		end

		def add_data( in_val )
			@probes += 1
			@total += in_val.to_f
			@probe_value = @total / @probes
		end

		def <=> (comp_probe)
			if(@loc_obj && comp_probe.loc_obj)
				@loc_obj <=> comp_probe.loc_obj
			else
				if(@loc_obj)
					1
				else
					if(comp_probe.loc_obj)
						-1
					else
						0
					end
				end
			end
		end
	end

end
